package com.yilian.bionio;

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

/**
 * BIO阻塞特性验证演示类
 * 
 * 这个类通过实际的网络IO操作来验证BIO的阻塞特性：
 * 1. 服务器accept()阻塞 - 等待客户端连接
 * 2. 客户端connect()阻塞 - 等待连接建立
 * 3. 读写操作阻塞 - 等待数据可用
 * 4. 多客户端并发阻塞 - 展示线程模型
 * 
 * 学习要点：
 * - BIO的同步阻塞特性
 * - 一个连接一个线程的模型
 * - 阻塞对性能的影响
 * - 如何观察阻塞行为
 * 
 * @author 练习项目
 * @version 1.0
 */
public class BIOBlockingDemo {
    
    private static final int PORT = 9999;
    private static final String HOST = "localhost";
    
    public static void main(String[] args) {
        System.out.println("=== BIO阻塞特性验证演示 ===\n");
        
        // 1. 演示服务器accept()阻塞
        demonstrateServerAcceptBlocking();
        
        // 2. 演示客户端connect()阻塞
        demonstrateClientConnectBlocking();
        
        // 3. 演示读写操作阻塞
        demonstrateReadWriteBlocking();
        
        // 4. 演示多客户端并发阻塞
        demonstrateConcurrentBlocking();
        
        System.out.println("\n=== BIO阻塞特性验证完成 ===");
    }
    
    // ==================== 服务器accept()阻塞演示 ====================
    
    /**
     * 演示服务器accept()方法的阻塞特性
     * accept()会一直阻塞，直到有客户端连接
     */
    private static void demonstrateServerAcceptBlocking() {
        System.out.println("--- 服务器accept()阻塞演示 ---");
        
        // 启动BIO服务器
        BIOServer server = new BIOServer(PORT);
        server.start();
        
        try {
            // 等待服务器启动
            Thread.sleep(1000);
            
            System.out.println("1. 服务器已启动，正在等待客户端连接...");
            System.out.println("   注意：此时服务器线程在accept()方法上阻塞");
            System.out.println("   服务器日志会显示：'等待客户端连接...'");
            
            // 等待一段时间，让用户观察阻塞状态
            System.out.println("\n2. 等待5秒，观察服务器阻塞状态...");
            Thread.sleep(5000);
            
            // 现在连接一个客户端，解除阻塞
            System.out.println("\n3. 现在连接一个客户端，解除accept()阻塞...");
            connectClientAndSendMessage("测试客户端1", "Hello Server!");
            
            // 等待一段时间
            Thread.sleep(2000);
            
            // 停止服务器
            server.stop();
            
        } catch (InterruptedException e) {
            System.err.println("演示被中断: " + e.getMessage());
        }
    }
    
    // ==================== 客户端connect()阻塞演示 ====================
    
    /**
     * 演示客户端connect()方法的阻塞特性
     * connect()会阻塞直到连接建立或超时
     */
    private static void demonstrateClientConnectBlocking() {
        System.out.println("\n--- 客户端connect()阻塞演示 ---");
        
        // 启动BIO服务器
        BIOServer server = new BIOServer(PORT + 1);
        server.start();
        
        try {
            // 等待服务器启动
            Thread.sleep(1000);
            
            System.out.println("1. 服务器已启动，端口: " + (PORT + 1));
            
            // 演示连接成功的情况
            System.out.println("\n2. 演示连接成功（非阻塞）:");
            connectClientAndSendMessage("成功客户端", "连接成功!");
            
            // 演示连接失败的情况（连接不存在的服务器）
            System.out.println("\n3. 演示连接失败（会阻塞直到超时）:");
            System.out.println("   尝试连接到不存在的服务器...");
            
            // 在后台线程中尝试连接，避免阻塞主线程
            ExecutorService executor = Executors.newSingleThreadExecutor();
            executor.submit(() -> {
                try {
                    System.out.println("   开始连接...");
                    Socket socket = new Socket("localhost", 99999); // 不存在的端口
                    System.out.println("   连接成功（这行不应该出现）");
                    socket.close();
                } catch (IOException e) {
                    System.out.println("   连接失败: " + e.getMessage());
                }
            });
            
            // 等待连接尝试
            Thread.sleep(3000);
            
            // 关闭执行器
            executor.shutdown();
            
            // 停止服务器
            server.stop();
            
        } catch (InterruptedException e) {
            System.err.println("演示被中断: " + e.getMessage());
        }
    }
    
    // ==================== 读写操作阻塞演示 ====================
    
    /**
     * 演示读写操作的阻塞特性
     * read()会阻塞直到有数据可读
     * write()通常不阻塞，但可能因为缓冲区满而阻塞
     */
    private static void demonstrateReadWriteBlocking() {
        System.out.println("\n--- 读写操作阻塞演示 ---");
        
        // 启动BIO服务器
        BIOServer server = new BIOServer(PORT + 2);
        server.start();
        
        try {
            // 等待服务器启动
            Thread.sleep(1000);
            
            System.out.println("1. 服务器已启动，端口: " + (PORT + 2));
            
            // 演示读取阻塞
            System.out.println("\n2. 演示读取阻塞:");
            System.out.println("   客户端连接后，服务器会等待客户端发送数据");
            System.out.println("   如果没有数据，read()会一直阻塞");
            
            // 连接客户端但不发送数据
            Socket clientSocket = new Socket(HOST, PORT + 2);
            System.out.println("   客户端已连接，但不发送数据");
            
            // 等待一段时间，让用户观察阻塞状态
            Thread.sleep(3000);
            
            // 现在发送数据，解除读取阻塞
            System.out.println("\n3. 现在发送数据，解除读取阻塞:");
            PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
            writer.println("解除阻塞的数据");
            System.out.println("   数据已发送");
            
            // 等待一段时间
            Thread.sleep(2000);
            
            // 关闭连接
            clientSocket.close();
            
            // 停止服务器
            server.stop();
            
        } catch (IOException | InterruptedException e) {
            System.err.println("演示异常: " + e.getMessage());
        }
    }
    
    // ==================== 多客户端并发阻塞演示 ====================
    
    /**
     * 演示多客户端并发时的阻塞特性
     * 每个客户端连接都会创建一个新线程
     * 展示BIO的"一个连接一个线程"模型
     */
    private static void demonstrateConcurrentBlocking() {
        System.out.println("\n--- 多客户端并发阻塞演示 ---");
        
        // 启动BIO服务器
        BIOServer server = new BIOServer(PORT + 3);
        server.start();
        
        try {
            // 等待服务器启动
            Thread.sleep(1000);
            
            System.out.println("1. 服务器已启动，端口: " + (PORT + 3));
            System.out.println("   服务器使用线程池处理多个客户端连接");
            
            // 创建多个客户端
            System.out.println("\n2. 创建5个客户端连接:");
            ExecutorService clientExecutor = Executors.newFixedThreadPool(5);
            
            for (int i = 1; i <= 5; i++) {
                final int clientId = i;
                clientExecutor.submit(() -> {
                    try {
                        System.out.println("   客户端" + clientId + " 开始连接...");
                        Socket socket = new Socket(HOST, PORT + 3);
                        System.out.println("   客户端" + clientId + " 连接成功");
                        
                        // 发送消息
                        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
                        writer.println("来自客户端" + clientId + "的消息");
                        
                        // 读取响应
                        BufferedReader reader = new BufferedReader(
                            new InputStreamReader(socket.getInputStream()));
                        String response = reader.readLine();
                        System.out.println("   客户端" + clientId + " 收到响应: " + response);
                        
                        // 保持连接一段时间
                        Thread.sleep(2000);
                        
                        // 关闭连接
                        socket.close();
                        System.out.println("   客户端" + clientId + " 连接关闭");
                        
                    } catch (IOException | InterruptedException e) {
                        System.err.println("   客户端" + clientId + " 异常: " + e.getMessage());
                    }
                });
            }
            
            // 等待所有客户端完成
            clientExecutor.shutdown();
            clientExecutor.awaitTermination(10, TimeUnit.SECONDS);
            
            System.out.println("\n3. 所有客户端连接完成");
            System.out.println("   观察：每个客户端连接都创建了独立的线程");
            System.out.println("   这就是BIO的'一个连接一个线程'模型");
            
            // 停止服务器
            server.stop();
            
        } catch (InterruptedException e) {
            System.err.println("演示被中断: " + e.getMessage());
        }
    }
    
    // ==================== 辅助方法 ====================
    
    /**
     * 连接客户端并发送消息
     */
    private static void connectClientAndSendMessage(String clientName, String message) {
        try (Socket socket = new Socket(HOST, PORT);
             PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            
            System.out.println("   " + clientName + " 连接成功");
            
            // 发送消息
            writer.println(message);
            System.out.println("   " + clientName + " 发送: " + message);
            
            // 读取响应
            String response = reader.readLine();
            System.out.println("   " + clientName + " 收到: " + response);
            
        } catch (IOException e) {
            System.err.println("   " + clientName + " 连接失败: " + e.getMessage());
        }
    }
    
    // ==================== 内部类：BIO服务器 ====================
    
    /**
     * BIO服务器实现
     * 用于演示BIO的阻塞特性
     */
    private static class BIOServer {
        private final int port;
        private ServerSocket serverSocket;
        private ExecutorService executor;
        private volatile boolean running = false;
        
        public BIOServer(int port) {
            this.port = port;
        }
        
        public void start() {
            try {
                serverSocket = new ServerSocket(port);
                executor = Executors.newCachedThreadPool();
                running = true;
                
                System.out.println("BIO服务器启动，端口: " + port);
                
                // 启动服务器监听线程
                new Thread(() -> {
                    while (running) {
                        try {
                            System.out.println("   等待客户端连接...");
                            Socket clientSocket = serverSocket.accept();
                            System.out.println("   接受新连接: " + clientSocket.getInetAddress());
                            
                            // 为每个客户端连接创建一个新线程
                            executor.submit(() -> handleClient(clientSocket));
                        } catch (IOException e) {
                            if (running) {
                                System.err.println("   服务器异常: " + e.getMessage());
                            }
                        }
                    }
                }).start();
                
            } catch (IOException e) {
                System.err.println("BIO服务器启动失败: " + e.getMessage());
            }
        }
        
        private void handleClient(Socket clientSocket) {
            try (PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
                 BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
                
                System.out.println("   处理客户端连接: " + clientSocket.getInetAddress());
                
                String message;
                while ((message = reader.readLine()) != null) {
                    System.out.println("   收到消息: " + message);
                    
                    // 发送响应
                    String response = "服务器响应: " + message;
                    writer.println(response);
                    System.out.println("   发送响应: " + response);
                }
                
                System.out.println("   客户端断开连接: " + clientSocket.getInetAddress());
                
            } catch (IOException e) {
                System.err.println("   处理客户端异常: " + e.getMessage());
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    // 忽略关闭异常
                }
            }
        }
        
        public void stop() {
            running = false;
            if (executor != null) {
                executor.shutdown();
            }
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                    System.out.println("   服务器已停止");
                } catch (IOException e) {
                    System.err.println("   停止服务器异常: " + e.getMessage());
                }
            }
        }
    }
} 